// ============================================================================
// Core 1
// 
// Description: 
// Tile 1 processes the second half of the image, starting from line 160, going
// until the end. The data is received from Tile 2 using the shared memory, 
// filtered and returned to the shared memory again.
// 
// ============================================================================

#include "memmap.h"
#include "compsoc.h"

#include "../filter_ex3_common/structures.h"
#include "../filter_ex3_common/filter.h"


#define TILE_ID (1)

// ============================================================================
// Data mapping
// ============================================================================

// Use the global memory as the task queue
const Queue* taskQueue = (void*) shared_mem_pt;

// Use rdma0 as a temporary slot
DMA dma0 = {1, (void*) dma0_BASEADDR};
Slot* tempSlot0 = (void*) cmemout0_BASEADDR;

// Use rdma1 as a temporary slot state
DMA dma1 = {2, (void*) dma1_BASEADDR};
SlotState* stateTemp = (void*) cmemout1_BASEADDR;

// Use synchronization register as queue state
const TileQueueState* stateTileQueue = (void*) cmemin0_BASEADDR;

// Use tile 2 synchronization registers as queue state
const QueueState* stateQueue = (void*) mb2_cmemin0_pt;

// Use Dmem as a local buffer
Row localBuffer[7];

// ============================================================================
// Transfer functions
// ============================================================================

void copyRows(Row* source, Row* destination, int numberOfRows) {

	int x;
	for (x = 0; x < IMAGE_WIDTH * numberOfRows; x++) {
		destination->pixels[x] = source->pixels[x];
	}
}

void receiveRows(Row* buffer, DMA* dma, int slot) {

	hw_dma_receive((void*) buffer, (void*) &taskQueue->tiles[TILE_ID].slots[slot], sizeof(Slot) / 4, dma);
}

void sendRows(Row* buffer, DMA* dma, int slot, int numberOfRows) {

	hw_dma_send((void*) &taskQueue->tiles[TILE_ID].slots[slot], (void*) buffer, sizeof(Slot) / 4, dma);
}

// Write slot state
void updateQueueSlot(int slot, SlotState newState) {

	*stateTemp = newState;

	hw_dma_send((void*) &stateQueue->tiles[TILE_ID].slots[slot], (void*) stateTemp, sizeof(SlotState) / 4, &dma1);
}

// ============================================================================
// Main entry point
// ============================================================================

int main(){

	// Ensure platform initialisation has completed
	mon_sync(0);
	// Debug line for started.
	print_debug(1);	
	
	// This tile starts filtering at 160.
	int line = 160;
	unsigned int i = 0;

	while(1) {
		for (i = 0; i < QUEUE_LENGTH; i++) {
			// The last line doesn't need any new data, only a small difference in processing.			
			if (stateTileQueue->slots[i].state == STATE_CORE1END){
				//Filter the last received row.				
				print_debug(10);						
				filterRow(&localBuffer[5], &localBuffer[6], &localBuffer[6], &tempSlot0->rows[0], line-1);
				print_debug(13);
				//Writes it to the shared memory
				sendRows(&tempSlot0->rows[0], &dma0, i, 1);
				// Update state
				SlotState newState;
				newState.state = STATE_PROCESSED1;
				newState.startRow = stateTileQueue->slots[i].startRow;
				updateQueueSlot(i, newState);
				print_debug(14);
				return 0;
			// The first step is just to load a previous line. (Line 159 is processed by tile0, but is necessary for tile 1.
			}else if (stateTileQueue->slots[i].state == STATE_CORE1START){
				print_debug(20);				
				// Read the rows out of shared memory.
				receiveRows(&tempSlot0->rows[0], &dma0, i);
				print_debug(21);				
				copyRows(&tempSlot0->rows[0], &localBuffer[1], 1);
				// Update state
				SlotState newState;
				newState.state = STATE_EMPTY;
				newState.startRow = stateTileQueue->slots[i].startRow;
				print_debug(22);				
				updateQueueSlot(i, newState);
				print_debug(23);
			// Filter lines.			
			}else if (stateTileQueue->slots[i].state == STATE_UNPROCESSED && 
  			    stateTileQueue->slots[i].startRow == line) {
				
				// New state defined.
				SlotState newState;
				newState.state = STATE_PROCESSED4; // Could be changed to STATE_PROCESSED, if this is not the first line.
				newState.startRow = stateTileQueue->slots[i].startRow;

				// Receive the rows.
				print_debug(30);			
				receiveRows(&tempSlot0->rows[0], &dma0, i);
				print_debug(31);				
				copyRows(&tempSlot0->rows[0], &localBuffer[2], 5);
				print_debug(32);	
		
				// If it's not the first row, we can filter 5 lines. If it is, only 4.
				if (line > 160) {		
					filterRow(&localBuffer[0], &localBuffer[1], &localBuffer[2], &tempSlot0->rows[0], line - 1);	
					newState.state = STATE_PROCESSED;				
				}

				filterRow(&localBuffer[1], &localBuffer[2], &localBuffer[3], &tempSlot0->rows[1], line + 0);
				filterRow(&localBuffer[2], &localBuffer[3], &localBuffer[4], &tempSlot0->rows[2], line + 1);
				filterRow(&localBuffer[3], &localBuffer[4], &localBuffer[5], &tempSlot0->rows[3], line + 2);
				filterRow(&localBuffer[4], &localBuffer[5], &localBuffer[6], &tempSlot0->rows[4], line + 3);
				
				print_debug(33);	

				// Copy rows
				copyRows(&localBuffer[5], &localBuffer[0], 2);

				print_debug(34);	

				// Send rows
				if (line == 160) {
					sendRows(&tempSlot0->rows[1], &dma0, i, 4);
				} else {
					sendRows(&tempSlot0->rows[0], &dma0, i, 5);
				}

				print_debug(35);	

				line += 5;
				// Actually update state.
				updateQueueSlot(i, newState);
			
				print_debug(36);	

			}
		}
	}

	return 0;
}
